#include "dicom/handler/dir_op_handler.h"

#include <boost/bind.hpp>
#include <gwbase/base/util.h>
#include <gwbase/base/logging.h>
#include <json/json.h>
#include "dicom/web_server.h"
#include "dicom/session.h"
#include "dicom/dmv_db.h"
#include "dicom/handler/handler_common.h"
#include <surbasic/surbasic.h>

namespace dicom {

static HttpHandler* CreateDirOpHander(onion_request* conn,
                                      DirOpHandler::DirOpType op) {
  return new DirOpHandler(conn, op);
}

void DirOpHandler::RegisterHandler(WebServer* s) {
  s->AddHandler("/api/folder/create", "GET",
                boost::bind(CreateDirOpHander, _1, kDirOp_Create));

  s->AddHandler("/api/folder/delete", "GET",
                boost::bind(CreateDirOpHander, _1, kDirOp_Delete));

  /*
  s->AddHandler("/api/folder/move", "GET",
                boost::bind(CreateDirOpHander, _1, kDirOp_Move));
  */

  s->AddHandler("/api/folder/rename", "GET",
                boost::bind(CreateDirOpHander, _1, kDirOp_Rename));
}

int DirOpHandler::HandleRequest(onion_request* conn, onion_response* resp) {
  Status s;
  CredentialPtr c;

  s = CheckToken(conn, &c);
  if (!s.ok()) {
    SendSimpleResponse(resp, 401, "oauth error");
    return OCS_PROCESSED;
  }

  if (op_type_ == kDirOp_Create) {
    return HandleCreate(conn, resp, c);
  } else if (op_type_ == kDirOp_Delete) {
    return HandleDelete(conn, resp, c);
  } else if (op_type_ == kDirOp_Rename) {
    return HandleRename(conn, resp, c);
  } else {
    return HandleMove(conn, resp, c);
  }

  SendSimpleResponse(resp, 400, "bad request");
  return OCS_PROCESSED;
}

int DirOpHandler::HandleCreate(onion_request* conn, onion_response* resp,
                               const CredentialPtr& c) {
  std::string pid = GetQueryString(conn, "pid");
  std::string name = GetQueryString(conn, "name");

  LOG_TRACE << "create folder: " << pid << "/" << name;

  if (pid.empty() || name.empty()) {
    SendSimpleResponse(resp, 400, "pid or name not set");
    return OCS_PROCESSED;
  }

  DmvDBPtr db = GetGateway()->dmv_mgr()->GetDB(c->email);
  EntryLockGuard entry_lock(db, pid, name);
  DirObject pd;
  if (db->GetDirByID(pid, &pd) == false) {
    SendSimpleResponse(resp, 400, "parent folder not found");
    return OCS_PROCESSED;
  }

  DirObject query;
  if (db->GetSubDirByName(pid, name, &query) == true) {
    SendSimpleResponse(resp, 400, "folder already exists");
    return OCS_PROCESSED;
  }

  // first create it in surcloud
  /*
  SDBasic::TreeEntity node;
  bool ret = SDBasic::CreateFolder(c->surdoc_key,
                                   pd.cloud_id,
                                   name,
                                   node);
  if (ret == false) {
    SendSimpleResponse(resp, 500, "failed to create folder in surcloud");
    return OCS_PROCESSED;
  }
  */

  LogHippaOperation(OP_CREATE_DIR, db, c, pid, name);

  DirObject d;
  d.local_id = GenFileId();
  d.cloud_id = "";
  d.parentId = pid;
  d.name = name;
  d.is_delete = 0;
  d.creator = c->email;
  d.createTime = d.modifiedTime = Timestamp::Now();
  db->UpdateDir(d);

  Json::Value v;
  v["code"] = 0;
  v["message"] = "success";
  Json::Value item;
  d.ToJson(item);
  v["item"] = item;
  db->UpdateParentDirModifyTime(pid);
  SendJsonResult(resp, 200, v);
  return OCS_PROCESSED;
}

int DirOpHandler::HandleRename(onion_request* conn, onion_response* resp,
                               const CredentialPtr& c) {
  DmvDBPtr db = GetGateway()->dmv_mgr()->GetDB(c->email);
  std::string id = GetQueryString(conn, "id");
  std::string name = GetQueryString(conn, "name");

  EntryLockGuard entry_lock_self(db, id, ".");

  if (id.empty() || name.empty()) {
    SendSimpleResponse(resp, 400, "id or name not set");
    return OCS_PROCESSED;
  }

  DirObject d;
  if (db->GetDirByID(id, &d) == false) {
    SendSimpleResponse(resp, 400, "folder not found");
    return OCS_PROCESSED;
  }

  LogHippaOperation(OP_RENAME_DIR, db, c, d.parentId, d.name, d.parentId, name);

  DirObject query;
  if (db->GetSubDirByName(d.parentId, name, &query) == true) {
    SendSimpleResponse(resp, 400, "folder already exists");
    return OCS_PROCESSED;
  }

  // now try to rename in surcloud
  if (!d.cloud_id.empty()) {
    SDBasic::TreeEntity node;
    bool result = SDBasic::RenameFolder(c->surdoc_key, d.cloud_id, name, node);
    if (result == false) {
      SendSimpleResponse(resp, 500, "failed to rename folder in surcloud");
      return OCS_PROCESSED;
    }
  }

  d.name = name;
  d.modifiedTime = Timestamp::Now();
  db->UpdateDir(d);

  Json::Value v;
  v["code"] = 0;
  v["message"] = "success";
  Json::Value item;
  d.ToJson(item);
  v["item"] = item;

  db->UpdateParentDirModifyTime(d.parentId);

  SendJsonResult(resp, 200, v);
  return OCS_PROCESSED;
}

int DirOpHandler::HandleDelete(onion_request* conn, onion_response* resp,
                               const CredentialPtr& c) {
  DmvDBPtr db = GetGateway()->dmv_mgr()->GetDB(c->email);
  std::string id = GetQueryString(conn, "id");

  EntryLockGuard entry_lock_self(db, id, ".");

  DirObject d;
  if (!db->GetDirByID(id, &d)) {
    SendSimpleResponse(resp, 404, "dir not found");
    return OCS_PROCESSED;
  }

  LogHippaOperation(OP_DELETE_DIR, db, c, d.parentId, d.name);

  std::vector<DirObject> dirs;
  std::vector<FileObject> files;
  db->GetSubItems(id, &dirs, &files);
  if (!dirs.empty() || !files.empty()) {
    SendSimpleResponse(resp, 400, "folder is not empty");
    return OCS_PROCESSED;
  }

  if (!d.cloud_id.empty()) {
    bool result = SDBasic::DeleteFolder(c->surdoc_key, d.cloud_id, true);
    if (result == false) {
      SendSimpleResponse(resp, 500, "failed to delete folder in surcloud");
      return OCS_PROCESSED;
    }
  }

  db->DeleteDir(id);
  Json::Value v;
  v["code"] = 0;
  v["message"] = "success";
  SendJsonResult(resp, 200, v);

  db->UpdateParentDirModifyTime(d.parentId);
  return OCS_PROCESSED;
}

int DirOpHandler::HandleMove(onion_request* conn, onion_response* resp,
                             const CredentialPtr& c) {
  std::string id = GetQueryString(conn, "id");
  std::string pid = GetQueryString(conn, "pid");

  if (id.empty() || pid.empty()) {
    SendSimpleResponse(resp, 400, "id or pid not set");
    return OCS_PROCESSED;
  }

  DmvDBPtr db = GetGateway()->dmv_mgr()->GetDB(c->email);
  DirObject pd;
  if (db->GetDirByID(id, &pd) == false) {
    SendSimpleResponse(resp, 400, "folder not found");
    return OCS_PROCESSED;
  }

  DirObject query;
  if (db->GetSubDirByName(pid, pd.name, &query) == true) {
    SendSimpleResponse(resp, 400, "folder already exists");
    return OCS_PROCESSED;
  }

  db->UpdateParentDirModifyTime(pd.parentId);

  pd.parentId = pid;
  pd.modifiedTime = Timestamp::Now();
  db->UpdateDir(pd);

  Json::Value v;
  v["code"] = 0;
  v["message"] = "success";
  Json::Value item;
  pd.ToJson(item);
  v["item"] = item;

  db->UpdateParentDirModifyTime(pid);

  SendJsonResult(resp, 200, v);
  return OCS_PROCESSED;
}
}
